import React, { useState, useRef } from "react";
import styles from "./BigFileUploadDD.less";
import cls from "classnames";
import { UploadOutlined, TagsOutlined, DeleteOutlined } from "@ant-design/icons";
import { Progress, Spin } from "antd";
import request from "../utils/request";
import SparkMD5 from "spark-md5";

type Props = {
  /** 分片文件上传url */
  uploadUrl: string;
  title?: string;
  /** 切分文件大小, 默认 5m = 5 * 1024 * 1024 */
  splitFileSize?: number;

  className?: string;
  style?: React.CSSProperties;

  disabled?: boolean;
  value?: string;
  onChange?: (val: string) => void;
};

/**
 * [author 邓东东]
 *
 * 大文件分片上传组件，内置两种模式，小于 splitFileSize 的文件直接上传，大于 splitFileSize 的文件分片上传，并显示进度条
 *
 * 依赖：
 *   1. 计算md5用的库：spark-md5
 * */
export default function BigFileUploadDD({
  uploadUrl,
  splitFileSize = 5 * 1024 * 1024,
  disabled = false,
  title = "上传文件",
  className,
  style,
  value,
  onChange,
}: Props) {
  const [fileName, setFileName] = useState(null);
  const [loading, setLoading] = useState(false);
  const [mode, setMode] = useState<"big" | "normal">("normal");
  const [progress, setProgress] = useState(null);
  const fileInputRef = useRef<HTMLInputElement | null>(null);

  const handleClick = () => {
    if (fileInputRef && !disabled) {
      fileInputRef.current.click();
    }
  };

  const handleFileChange = async (event: React.ChangeEvent<HTMLInputElement>) => {
    const file = event.target.files?.[0];
    setFileName(null);

    if (file) {
      try {
        if (file.size > splitFileSize) {
          // 分片上传
          setMode("big");
          await handleLargeFileUpload(file);
        } else {
          setMode("normal");
          // 单文件上传
          const formData = new FormData();
          formData.append("file", file);
          formData.append("chunkNumber", String(1));
          formData.append("md5", await getChunkMD5(file));
          formData.append("totalChunks", String(1));

          setLoading(true);
          const response = await request.post(uploadUrl, formData, {
            headers: {
              "Content-Type": "multipart/form-data",
            },
          });

          if (response.status === 200) {
            console.log("Upload successful:", response.data);
            setFileName(file.name);
            onChange?.(file.name);
          } else {
            console.error("Upload failed:", response.statusText);
          }
          setLoading(false);
        }
      } catch (error) {
        console.error("Error uploading file:", error);
      }
    }
  };

  const handleLargeFileUpload = async (file: File) => {
    let totalUploaded = 0;
    const chunks = [];
    const chunkMD5s = [];

    setProgress(0);

    // 分割文件
    for (let i = 0; i < file.size; i += splitFileSize) {
      const chunk = file.slice(i, Math.min(i + splitFileSize, file.size));
      const chunkIndex = Math.floor(i / splitFileSize);
      chunks.push(chunk);
      chunkMD5s.push(await getChunkMD5(chunk)); // 获取分片的 MD5 值
    }

    for (let i = 0; i < chunks.length; i++) {
      const chunk = chunks[i];
      const chunkIndex = i;
      const chunkMD5 = chunkMD5s[i];

      const formData = new FormData();
      formData.append("file", chunk, file.name);
      formData.append("chunkNumber", String(chunkIndex + 1));
      formData.append("md5", chunkMD5);
      formData.append("totalChunks", String(chunks.length));

      const response = await request.post(uploadUrl, formData, {
        headers: {
          "Content-Type": "multipart/form-data",
        },
      });

      if (response.status === 200) {
        totalUploaded += chunk.size;
        setProgress(((totalUploaded / file.size) * 100).toFixed(2));
      } else {
        console.error("大文件上传分片出错:", response.statusText);
        break;
      }
    }

    if (totalUploaded === file.size) {
      console.log("所有分片上传完毕");
      setFileName(file.name);
      onChange?.(file.name);
      setProgress(null);
    }
  };

  // const getChunkMD5 = async (chunk: Blob): Promise<string> => {
  //   const reader = new FileReader();
  //   return new Promise((resolve, reject) => {
  //     reader.onloadend = () => {
  //       const arrayBuffer = reader.result as ArrayBuffer;
  //       const uint8Array = new Uint8Array(arrayBuffer);
  //       crypto.subtle.digest("SHA-256", uint8Array).then((hashBuffer) => {
  //         const hashArray = Array.from(new Uint8Array(hashBuffer));
  //         const hashHex = hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
  //         resolve(hashHex);
  //       });
  //     };
  //     reader.onerror = reject;
  //     reader.readAsArrayBuffer(chunk);
  //   });
  // };

  const getChunkMD5 = (chunk: Blob): Promise<string> => {
    return new Promise((resolve, reject) => {
      const reader = new FileReader();
      const spark = new SparkMD5.ArrayBuffer();
      reader.onload = (e) => {
        spark.append(e.target.result as ArrayBuffer);
        resolve(spark.end());
      };
      reader.onerror = (e) => {
        reject(e);
      };
      reader.readAsArrayBuffer(chunk);
    });
  };

  const onDelete = () => {
    setFileName(null);
    setLoading(false);
    setMode("normal");
    setProgress(null);
    onChange?.(null);
  };

  const renderProgress = () => {
    if (mode === "big") {
      return progress != null ? (
        <div className={styles.progressBar}>
          <Progress percent={progress} />
        </div>
      ) : null;
    } else {
      return loading ? <Spin /> : null;
    }
  };

  return (
    <div className={cls(styles.BigFileUploadDD, className)} style={style}>
      <input type="file" accept="" style={{ display: "none" }} ref={fileInputRef} onChange={handleFileChange} />

      <button type="button" className={styles.innerBtn} onClick={handleClick}>
        <UploadOutlined />
        <span className={styles.title}>{title}</span>
      </button>

      {/* 分片大文件，需要显示进度条 */}
      {renderProgress()}

      {fileName != null ? (
        <div className={styles.fileList}>
          <TagsOutlined />
          <div className={styles.fileName} title={fileName}>
            {fileName}
          </div>
          <DeleteOutlined style={{ color: "red", cursor: "pointer" }} title={"删除"} onClick={onDelete} />
        </div>
      ) : null}
    </div>
  );
}
